#set document(title: [makec - 编译成 C 语言的编程语言])
#set heading(numbering: "1.1 -")

#outline(indent: auto) <outline>

= 简介

makec 是编译成 C 语言的编程语言，目前我选择的 C 版本是 C99。通过编译，我可以引入更多的检查和语言层次的抽象能力，而不需要引入过多的额外开销。makec 的编译目标确定在 C 上，而依赖 C 编译器进行本地化编译，makec 因此将工具和设计重点集中在语义上，而不需要过多关注运行时。

第一个阶段的编译器是 Rust 写成的 makec-stage0。现在的开发目标是使用这个编译器编译出 makec 的编译器。

makec-stage0 会读取 stage1.mkc，编译出 stage1.c。stage1.c 意图包含下一阶段 的 makec 编译器，但现在只有一些基本的语言测试和解析函数。stage1.c 可以用 C 编译器编译成本地程序，现在可以阅读、运行和探索这些测试。

如果你安装了 Rust 编译器，那只需运行下面的命令就可以完成生成 stage1.c、编译出 stage1 编译器和运行编译器的测试的过程。

```sh
./makec -r stage1.mkc
```

`./snaptest.sh` 将在 `tests/*.mkc` 上测试 stage0 编译器的输出是否跟对应的 `tests/*.mkc.expect.c` 一致。这些测试点也可以视作是 makec 的语法示例。

== `makec` 脚本

```
makec [-c] [-r] file.mkc...
```

`makec` 脚本编译 stage0 编译器，将 file1.mkc 编译为 file1.c。如果有 `-c` 选项，它会调用 `cc` 编译器将 `file.c` 编译为 `file.bin`。如果有 `-r` 选项（隐含了 `-c`），它会运行 `file.bin`。

== Logo

```
(X _ _)
```

这是我为 makec 设计的 logo，它好像一个有点愤怒和颓丧的表情。选择这个 logo 的原因是它可以展示几个 makec 的语法特性，这其实是一个合法的 makec 表达式的一部分，例如下面的表达式：

```
(X _ _)
| a
| 10
```

它的意思其实是 C 语言中的 `a * 10`。

首先，makec 对算数计算不采用中缀表达式，而采用前缀的，要表示 C 中的 `(a + b)`，你应该写 `(+ a b)`。

其次，makec 表示乘号的符号不是 `*` 而是 `X`，makec 里的 `*` 只有跟指针有关的意义。所以写 C 中的 `(a * b)`，你应该写 `(X a b)`。这里还有个潜在的特别之处，makec 中表示变量名的标识符不能包含大写字母，大写字母只能用在类型名里。所以把 `X` 当作乘号并没有牺牲一个变量标识符的可能性（因为本来 `X` 就不能做变量标识符），它只牺牲了你的自定义类型的名字是单独的 `X` 的情况。

最后，下划线（`_`）并不是个一般的标识符，它有一定的语法意义，它叫做「洞」，表示这个地方的表达式我还没写完，我想写在后面。而竖线（`|`）后跟着表达式的形式，就表示要用后面的表达式填上前面的洞。因此你应该可以理解 `(X _ _)` 了，它表示把两个表达式相乘，而这两个表达式都还没写完。

= makec 语法介绍

makec 还没有释放第一个版本，因此这些语法都可能会变化。

== 表达式和类型

makec 的表达式分为如下几类：

- 字面值（整数、浮点数、布尔值、字符串、数组字面值）
- 算术运算、比较运算、sizeof 表达式、cast 表达式
- 函数调用
- 宏调用
- 条件表达式
- 取地址、解引用访问字段和下标访问

每个表达式都有确定的类型。在介绍表达式的同时会介绍它们的类型。

== 整数字面值

makec 有多种不同的整数类型：

- `int`、`uint`：与机器指针长度一样的有符号和无符号整数类型，对应 C99 的 `intptr_t` 和 `uintptr_t`。
- `intN`、`uintN`：定长的整数类型，其中的 `N` 有 8、16、32、64。对应 C99 的 `intN_t` 和 `uintN_t`。

跟 C 一样，整数字面值通过后缀来区分不同的类型，例如说 `120u` 表示 `uint` 类型的 120。此外数字中间可以任意增加下划线来增加可读性，例如 `120u` 和 `120_u` 和 `1__20_u` 都是一样的。下面列出了不同类型的后缀和示例。

#table(
  columns: 3,
  [后缀],  [类型],     [例子],
  [`i`],   [`int`],    [`-19i`],
  [`u`],   [`uint`],   [`10u`],
  [`i32`], [`int32`],  [`32i32`],
  [`u32`], [`uint32`], [`128_u32`],
  [`i64`], [`int64`],  [`0i64`],
  [`u64`], [`uint64`], [`0u64`],
)


暂时还没有对 8 进制和 16 进制和其他进制数字字面值的支持。

不带后缀的数字，应该有一种依赖类型推断的活动类型，例如说 `0` 赋值给 `int32` 类型的时候，`0` 就等于 `0u32`，而赋值给 `=uint` 的时候，等于 `0u`。但我还没有做好 :-P。

== 其他字面值 <otherliteral>

`true` 和 `false` 是 `bool` 类型的字面值。`"HELLO"` 这样的字符串字面值是 `char const *` 类型的字面值，它表示 `char` 类型的常量的指针，这与 C 的类型基本上一样。`'X'` 这样的字符字面值是 `char` 类型。

字符和字符串字面值支持对 `\0`、`\t`、`\r`、`\n`、`\\` 的转义。

对浮点数的支持会有的，但还没有做。

== 基本类型语法 <basictype>

这里要插入一些对类型组合语法。我们已经看到了 `char const*` 表示指向 `const char` 的类型的指针类型，跟 C 的语法完全一样，但其实 makec 的规则略有不同。`const` 和 `*` 都是后置的修饰，`T const` 表示 T 的不可变变体，`T*` 表示指向 `T` 的指针类型。所以 makec 中 `const int` 这样的写法是不合法的，只能写成 `int const`。

当然 makec 还有自定义类型，结构体、联合体和枚举类型。跟 C 略有不同的是它们共用同一个名字空间，也就是说，结构体名和枚举名是不能重名的，同时引用类型名的时候不需要前缀 `struct` 和 `enum` 来区分当前在使用结构体还是枚举。

例如 `Foo const* const**` 表示  `Foo` 的 `const` 变体的指针的 `const` 变体的指针的指针。`Foo` 是结构体、枚举还是联合体要看 `Foo` 定义的位置是什么。

== 函数调用表达式

makec 使用类似 Lisp 语言族那样的函数调用语法：

```
(func_name arg1 arg2)
```

而不是 C 那样的：

```c
func_name(arg1, arg2)
```

基本上区别就是，将左括号提取到函数名前面，同时去掉分隔参数的逗号。

例如 `(malloc 10u)` 表示申请 10 个字节的内存，它的参数是 `uint` 类型的 10。

== 宏调用

我其实并没想好怎样给 makec 提供宏支持，目前只是在 stage0 的编译器里硬编码了几个宏，并且借鉴了 Rust 的宏函数调用语法，即在函数名后面加一个感叹号：

```
(print! a b c...)
```

`print!` 宏将参数依次打印到标准输出，根据参数的类型不同，它会展开成不同的函数。例如 64 位无符号整数使用 `printf("%" PRIu64, X)` 打印，而字符串使用 `fputs` 函数打印。这个表达式的值类型是 `void`。

```
(file!)
(line!)
```

会展开成 C 标准定义的 `__FILE__` 和 `__LINE__` 宏的值。

此外还有 `eprint!` 宏，除了输出到标准错误输出，其他跟 `print!` 一样。

== 其他类函数调用的表达式 <funclikeexpr>

算术计算和比较也使用 Lisp 族的语法，例如计算 10 和 12 的和，C 中是：

```c
(10 + 12)
```

而 makec 也遵循 Lisp 的写法：

```
(+ 10 12)
```

注意两种语法的括号意义是不同的，C 中对中缀表达式的括号是一种结合性的表达，在没有歧义的上下文中是可选的，而 Lisp 中的表示类似函数调用的语义，是不能省略的。makec（或者说 Lisp）的写法就好像在说 `+` 只不过是一个普通的函数，就用调用函数的语法调用它。

此外还有一点不同，makec 的计算是可以携带多个参数的：

```
(+ a b c d)
```

就等于 C 的：

```c
(a + b + c + d)
```

下面列出一些其他语法类似函数调用的表达式，涵盖了算术运算、比较、逻辑运算还有
sizeof 和类型转换：

- `(+ a b c ...)`：`(a + b + c + ...)`
- `(- a b c ...)`：`(a - b - c - ...)`
- `(X a b c ...)`：`(a * b * c * ...)`
- `(/ a b c ...)`：`(a / b / c / . ..)`
- `(% a b)`：`(a % b)`
- `(= a b)`：`(a == b)`
- `(> a b)`：`(a > b)`
- `(< a b)`：`(a < b)`
- `(>= a b)`：`(a >= b)`
- `(<= a b)`：`(a <= b)`
- `(!= a b)`：`(a != b)`
- `(not a)`：`(!a)`
- `(or a b c ...)`：`(a || b || c || ...)`
- `(and a b c ...)`：`(a && b && c && ...)`
- `(bshl x l)`：`(x << l)`
- `(bshr x l)`：`(x >> l)`
- `(cast type expr)`：`((type) expr)`
- `(sizeof type)`：`sizeof(type)`

注意我选择了 `not`、`and` 和 `or` 来表达逻辑运算，而不是 C 里面的 `!=`、`&&` 和 `||`。

== 条件表达式 <ifexpr>

条件表达式也表现为一个类似函数调用的样子，只是函数名的地方是 `if` 关键字，因此它不会跟函数调用混淆。跟 if 语句不同，它是一个表达式，因此有对应的类型。

```
(if cond1: val1
    cond2: val2
    ...
    else: valelse)
```

每个 `condN: argN`，表示当 `condN` 是 `true` 的时候，条件表达式的值是 `argN`。而 `else: valelse` 则表示前面条件都是 `false` 的时候条件表达式的值是 `valelse`。

如果只有 `cond1: val1` 和 `else: val2` 那么它实际上翻译成了 C 的三目条件表达式：

```
(if cond1: val1
    else: val2)
```

```c
(cond1) ? (val1) : (val2)
```

而有多个的时候它会翻译成嵌套的多个三目条件表达式。最终的达成的语义是：

- 条件表达式的所有分支的值 `val1`、`val2`、`valn` 的值的类型都必须相同，这个类型就是条件表达式的类型。
- 仅当 `condK` 是条件列表中第一个为 `true` 的条件的时候，才求值 `valK`，而其他分支都不会被求值。
- 如果所有条件都是 `false`，那么求值 `else` 之后的表达式，其他分支的表达式不被求值。
- 条件表达式的值是被求值的分支中的表达式的值。

=== 注：连续的表达式 <continueexpr>

这个语法会导致一种现象，例如下面的 if 表达式：

```
(if (< a 0): "negative" (> a 0): "positive" else: "zero")
```

`"negative"` 和 `(> a 0)` 是两个表达式，它们之间没有明显的分隔。这可能会导致阅读困难。

== 洞和子节点 <holechild>

makec 中单独的下划线不是普通的标识符，它有一些语法含义。表达式除了字面值，都是
`(a b c...)` 这样的形式，只是参数个数不同而已。这种东西在 Lisp 中叫 S 表达式。

makec 允许用 `_` 作为一个 S 表达式中的占位符，而这个值用 `| EXPR` 的形式后缀在 S 表达式后面。例如：

```
(add _ _) | 10 | 12
```

就等价于

```
(add 10 12)
```

makec 代码对换行并不敏感，因此后缀的部份可以换行并对齐，看起来就是这样：

```
(add _ _)
| 10
| 12
```

看起来就像是 10 和 12 这两个表达式作为 `(add _ _)` 函数调用的子节点，画出了一颗树形的图像。

这样的语法糖还可以嵌套，产生一个更大的表达式树，例如下面的代码是条件的先或后与，而每个条件都是一个函数调用：

```
(and _ _)
| (or _ _)
  | (cond1)
  | (cond2)
| (or _ _)
  | (cond3)
  | (cond4)
```

它等价于：

```
(and (or (cond1) (cond2)) (or (cond3) (cond4)))
```

或者模仿 Lisp 的惯用缩进方法，它是：

```
(and (or (cond1)
         (cond2))
     (or (cond3)
         (cond4)))
```

C 的写法是：

```c
(cond1() || cond2()) && (cond3() || cond4())
```

最后还是强调一点，这只是 makec 为了解除 Lisp 那样的过多的嵌套括号引入的语法糖，不论是否使用它，编译产出的 C 代码都没有任何区别。

作为一个示例，可以查看 `tests/08-builder.mkc`：

```
(person_age _ 18)
| (person_woman _)
  | (person_address _ "Beijing, China")
    | p.&
```

产生的 C 代码是：

```
person_age(person_woman(person_address(&p, "Beijing, China")), UINTPTR_C(18));
```

== 取地址、解引用、访问字段和访问下标

访问字段和下标都借用了 C 的语法：

```c
EXPR.NAME
EXPR[INDEX]
```

但取地址和解引用不同，C 的语法是：

```c
&EXPR
*EXPR
```

我选择了改成：

```
EXPR&
EXPR*
```

例如：

- `foo*.bar` 表示 foo
  解引用的地址处访问（结构体的）bar 字段。
- `foo.bar&` 表示 foo 的 bar 字段的地址。

因为 `foo*.bar` 这样的写法也没有多麻烦，所以我干脆取消了对 C 的 `foo->bar` 的语法的支持。

同时作为 S 表达式的取地址、解引用、字段访问和下标访问也是存在的：

```
(& EXPR)
(* EXPR)
(. EXPR NAME)
([] EXPR INDEX)
```

要注意的是，下标访问的 `INDEX` 只接受 `uint` 类型。

== 表达式的语法总结

表达式的语法形式分为三种：

- 原子：`123` 和 `true` 和 `a` 这样的直接写出的值，它们是单独的语法单位，不存在语法单位的组合。
- S 表达式：`(a b c...)`。S 表达式里的每一项可能是表达式、或者类型、或者其他占位的符号等。
- 带后缀修饰的表达式：`EXPR[INDEX]` 和 `EXPR.NAME` 等。它们是表达式后面跟随一些修饰符号之后派生出的表达式。需要指出的是，这些形式都有对应的 S 表达式形式。也就是不使用后缀修饰也可以，只是使用的话代码会更简洁一些。

== 数组和复杂的类型语法

我们已经知道了 `const` 和 `*` 的后置修饰规则。

类比于表达式被统一为“原子”和“S 表达式”和“表达式加上某些后置修饰”三种语法。类型形式上也是一样的“基本类型”和“类型构造子”和“类型加上某些后置修饰”。基本类型已经介绍过了，类型构造子是类似
S 表达的东西，只是括号改成了中括号：

```
[* TYPE]
[const TYPE]
[10 TYPE]
```

分别代表指向 `TYPE` 的指针、`TYPE` 的 `const` 变体和长度为 10 的 `TYPE` 类型的数组。也就是说 `char const*` 实际上等于 `[* [const char]]`，所以之前我说 `const` 不能放在类型前面也不完全对，只要你乐意加中括号，也是可以放前面的。

未来我可能会引入自定义的类型构造子，例如变长的范型容器数组的类型可能会写成
`[Vec int]`。当然可能是很远的未来了。

== 语句

语句表示一种特别的求值语义，并不是代表一个值，因此也不会有类型。两个语句之间一般也没有特别的分隔符，只是用空白分开了就可以。

多个连续的语句就组成一段语句块，表示顺序执行的多个语句，这些也都跟 C 没有很大分别。我们下文中都用 BLOCK 这个词来表示这里放着一些语句。

== let 语句 <letstat>

```
let name = expr
let name: type = expr
let name: type
```

定义变量 name，其类型为 type，值为 expr。当 type 省略的时候，name 的类型就是 expr 的类型。当 expr 省略的时候，name 的值是此类型的默认值。

实际上现如今我还没有设计好默认值系统，目前只是保留变量未初始化。

此外，makec 还对标识符的使用有比较严格的限制。C 的标识符限制为字母数字下划线，同时不能用数字开头的字符串。makec
在此基础上还限制：

- 标识符前后不能有下划线。
- 不能使用连续两个下划线（`__`）。
- 当标识符表示变量或者函数名，也包括结构体字段名时，不能使用大写字母。
- 当标识符表示类型，包括结构体名、枚举名、枚举的变体名和联合体名，不能使用下划线，同时必须用大写字母开头。

这基本上就限制了变量或函数名用 `foo_bar` 这样的 Snake case 命名，而类型用 `FooBar` 这样命名。注意这是语法上的限制，而不是语言编程风格上的建议。

== 赋值语句和计算赋值

```
EXPR_L = EXPR_R

EXPR_L += EXPR_R
EXPR_L -= EXPR_R
EXPR_L X= EXPR_R
EXPR_L /= EXPR_R
EXPR_R %= EXPR_R
```

除了不需要分号分隔之外，跟 C 一样。

== 类型兼容性

makec 完全没有自动的整数提升，如果你需要赋值一个类型的整数给另一个，你就需要
`(cast TYPE EXPR)`。

== if 语句 <ifstat>

```
if COND1 {
  BLOCK1
} elif COND2 {
  BLOCK2
} else {
  BLOCK_ELSE
}
```

它跟 C 中的 if-else 语句基本一样，COND 的部份都是不同的条件表达式，分支部份都是若干个语句，也有一些不同：

- 条件的部份不用强求一个括号，只需要一个表达式。
- 分支部份只有一个语句，大括号的部份也不能省略。
- 也因为上一点，C 中的 else if 分支我们要引入一个 elif 关键字来达成。
- elif 分支可以重复出现任意多次。

== while、break、continue 语句 <whilestat>

```
while COND {
  BLOCK
}
```

它也跟 C 几乎一样，同样是头部条件的括号不需要了，以及大括号不能省略这两点。另外 break 和 continue 语句也跟 C 中的语义一样。

== 函数定义和 return 语句 <func>

```
func (name arg1: Type1
           arg2: Type2...) Type {
  BLOCK
}
```

`name` 是函数名，`Type` 是函数返回类型，`arg` 是函数参数，标记它们类型的方法跟 `let` 语句的方式一样。`Type` 也可以不写，这表示函数返回 `void` 类型。

`func` 是个表示此处是函数定义的关键字，没有其他含义。函数会对应生成静态（static）的 C 函数，而想要公开的函数的话，需要在 `func` 前前缀 `pub` 关键字。

表示函数返回的语句是 return 语句：

```
return EXPR
return void
```

当函数返回值是 `void` 的时候，`return` 可以返回类型为 `void` 的表达式，也可以什么都不返回。但是为了一些形式上一致性的问题，在什么都不反返回的时候 makec 要求你写 `return void` 而不是 `return`。

跟 C 不同，makec 的函数定义没有先后的分别，你可以随便在当前函数里调用后文定义的函数。这实际上是因为 makec 为每个函数都生成了一份函数声明写到了最前面。

== 方法 <method>

方法是一种特别的函数，从结构上看作一个类型的成分。

```
func (Type::name arg1: Type1 arg2: Type2...) ReturnType {
  BLOCK
}
```

从形式上方法只是个名字特别的函数，它的名字是 `Type::name`，而 `Type` 是一个已经定义的类型。

使用方法也跟使用普通函数一样：

```
(Type::name arg1 arg2...)
```

只是有一种特别的语法糖，当 `arg1` 的类型是 `Type` 类型的变体的时候，可以写成：

```
arg1.(name arg2...)
```

这里的变体指的是：

- `Type` 类型本身是一个变体类型。
- const 修饰的一个变体类型。
- 一个变体的指针类型。

总之，从 `arg1` 的类型中，makec 可以推断出 `Type` 是什么类型，从而可以自己补全 `Type` 而不用写出来。因此可以达成这种函数调用的简写。

它在形式上跟 C++ 的成员函数调用比较相似：

```cpp
arg1.name(arg2, ...)
```

但是 makec 的规则实际上更简单，它只是单纯的语法变换。在第一个参数是指针类型的时候，它们就会表现出不同。我们先给出一个 C++ 的例子。

```cpp
class Foo {
public:
  void foo();
};

static void TestFoo() {
  auto f = new Foo;
  f->foo();
  delete f;
}
```

`f->foo()` 表现出传递给 `F::foo` 的参数实际上是 `F` 的引用。而在 makec 里：

```
func (Foo::foo f: Foo*) {}

func (test_foo) {
  let f: Foo

  f&.(foo)
}
```

注意因为 `foo` 接受的参数是 `Foo` 的指针，所以直接使用 S 表达式调用方法的情况下写法应该是：`(Foo::foo f&)`，从而在 `.(foo)` 之前是 `f&` 就不难理解了。

== 结构体定义、联合体定义、枚举定义 <struct>

```
struct NAME {
  FIELD1: TYPE1
  FIELD2: TYPE2
  ...
}
```

跟 C 相比它在字段结尾和大括号结尾没有末尾分号。此外就是字段和类型的位置关系也采取 let 语句那样的方式。

联合体（union）的定义方法跟结构体完全类似，只是 `struct` 改成了 `union` 而已。枚举则在修改关键字的情况下也省略类型。

```
enum NAME {
  VARIANT1
  VARIANT2
  ...
}
```

== switch 语句 <switchstat>

```
switch EXPR {
  V1: { BLOCK1 }
  V2: { BLOCK2 }
  ...
  else: { BLOCK_ELSE }
}
```

现在的 switch 语句只制作了对值是枚举值时的支持，其中的 V1、V2 等标识符需对应枚举的变体名称。else 分支表示输入枚举值的变体是列出的变体之外的情况下要求值的语句块。此外没有 C 那样的 fallthrough 语义，也就基本上等于：

```c
switch (EXPR) {
  case V1: { BLOCK1 } break;
  case V2: { BLOCK2 } break;
  ...
  default: { BLOCK_ELSE } break;
}
```

== 模块和泛型 <modulegeneric>

每个文件在 import 之前，需要用 module 语句声明模块。

```
module [foo arg..]
```

`foo` 是模块的名字，必须跟文件名（除去后缀）对齐。因此当前文件必须命名为 `foo.mkc`。

模块声明时可以接受参数（`arg`），在导入模块时，需要传入参数。例如：

```
import [foo isize]
```

表示将 `foo` 模块的 `arg` 参数设为 `isize` 类型。而 `import [foo]` 可以简写为 `import foo`。

可以通过模块的名字前缀在类型和函数之前引用模块中的内容。例如：

```
import foo

foo::Foo
foo::foo
```

同一个模块只要参数不同，就可以引入多次：

```
import [foo isize]
import [foo bool]
import [foo [foo isize]::Foo]
```

= 关键字索引 <keywords>

这里按照字典序列出 makec 的关键字用在哪个语法单元中。

- `and`: @funclikeexpr
- `break`: @whilestat
- `bshl`: @funclikeexpr
- `bshr`: @funclikeexpr
- `cast`: @funclikeexpr
- `const`: @basictype
- `continue`: @whilestat
- `elif`: @ifstat
- `else`: @ifstat, @ifexpr
- `enum`: @struct
- `false`: @otherliteral
- `func`: @func
- `if`: @ifstat, @ifexpr
- `let`: @letstat
- `not`: @funclikeexpr
- `or`: @funclikeexpr
- `return`: @func
- `sizeof`: @funclikeexpr
- `struct`: @struct
- `switch`: @switchstat
- `true`: @otherliteral
- `union`: @struct
- `while`: @whilestat
- `_`: @holechild

= 名字修饰规则

makec 通过名字修饰在 C 的函数和类型模型上模拟模块和方法的概念。makec 使用双下划线（`__`）分割模块名和模块内的对象（类型和函数）名。如果一个名字没有双下划线那么模块名就是 `main`。

而对模块内的对象：

- 以小写字母开头的是函数。
- 以大写字母开头的是类型或者方法。
  - 不模块含 `_` 的是类型。
  - 模块含下划线的是方法，下划线前的部分是类型名，下划线后的名字是方法名。

例如 `re__Pattern_match` 是 `re` 模块中的 `Pattern` 类型上绑定的方法 `match`，`re__compile` 是 `re` 包中的 `compile` 函数，`time__Duration` 是 `time` 模块中的 `Duration` 类型，`User_name` 是 `main` 包的 `User` 类型上绑定的 `name` 方法。

在自定义模块时，有两个模块名是禁用的。

- `main` 表示可执行程序的入口函数所在的模块。
- `mc` 表示 `makec` 编译器产生的函数所在的包。

== 主函数 <mainfunc>

主函数是程序的入口，它需要是 `pub` 修饰的无返回值而且无参数的函数：

```
pub func (main) {
  (println! "Hello")
}
```

C 的 main 函数的返回值表示进程退出状态，参数可以是指向命令行参数的字符串数组。现在 makec 还没有做好退出状态的支持和命令行参数的支持。

makec 中的主函数会被修饰为 C 中的 `mc__main` 函数，按照修饰规则，这表示 `mc` 包中叫做 `main` 的函数。如果模块中定义了主函数。`makec` 会生成 C 的主函数，并且调用 `mc__main`。

= 单元测试支持

`test_` 开头的无参数无返回值的函数私有函数（不用 `pub` 开头的）会收集到 `test_main` 函数里，执行这个函数就可以完成单元测试：

```
pub func (main) {
  (test_main)
}
```

= 更改日志

== v0-20241425

第一个版本。

再打 tag 的时候写错了日期，实际上它是 10 月 25 日发布的。
